package com.rybbaby.tss.core.utils;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FilenameFilter;
import java.io.InputStream;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Predicate;

import javax.imageio.ImageIO;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import com.rybbaby.tss.core.bean.TempUploadFile;
import com.rybbaby.tss.core.utils.ThumbnailsUtils.ThumbnailsResult;
import com.rybbaby.tss.core.utils.pojo.Size;
import com.rybbaby.tss.core.utils.pojo.TreeFile;

/**
 * @author 谢进伟
 * @description 文件上传工具
 * @createDate 2016年11月14日 下午11:44:35
 */
public class FileUploadUtil {
	
	/**
	 * 日子对象
	 **/
	private static Logger log = Logger.getLogger(FileUploadUtil.class);
	/**
	 * 图片文件格式
	 **/
	private static List<String> imageFileSuffix = new ArrayList<String>();
	/**
	 * 上传的临时文件记录
	 **/
	private static Map<String , TempUploadFile> tempFiles = new Hashtable<String , TempUploadFile>();
	/**
	 * 临时文件清理频率
	 **/
	private static long frequency = 24 * 60 * 60 * 1000L;// 默认每一天
	/**
	 * 临时文件缓存最大时间
	 **/
	private static long maxCacheTime = 60 * 60 * 1000L;// 1小时
	private static ThumbnailsResult thumbnail;
	
	static {
		// 读取资源值
		frequency = PropertiesReadUtil.getLongProperty("FileUploadUtil.frequency" , frequency);
		maxCacheTime = PropertiesReadUtil.getLongProperty("FileUploadUtil.frequency" , maxCacheTime);
		// 初始化常用图片格式
		imageFileSuffix.add(".gif");
		imageFileSuffix.add(".jpg");
		imageFileSuffix.add(".jpeg");
		imageFileSuffix.add(".bmp");
		imageFileSuffix.add(".png");
		// 启动临时文件自动删除定时任务
		try {
			SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd 23:59:59");
			SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			Date startDate = sdf2.parse(sdf1.format(new Date()));
			Timer timer = new Timer();
			timer.schedule(new TimerTask() {
				
				@Override
				public void run() {
					clearTempUploadFile(maxCacheTime);
				}
				
			} , startDate , frequency);// 每天零点清理临时文件
		} catch (ParseException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 上传文件
	 *
	 * @param request
	 *            请求对象
	 * @param groupName
	 *            临时文件分组名
	 * @param sizes
	 *            缩放尺寸,当isImg为true的时候此参数有效
	 * @param isImg
	 *            是否是图片类型
	 * @param imgMaxWidth
	 *            图片文件允许的最大宽度
	 * @param imgMaxHeight
	 *            图片文件允许的最大高度
	 * @return 临时上传文件对象
	 * @throws Exception
	 */
	public static TempUploadFile uploadFile(MultipartHttpServletRequest request , String sessionId , String groupName , List<Size> sizes , boolean isImg , Double imgMaxWidth , Double imgMaxHeight) throws Exception {
		if(StringUtils.isBlank(groupName)) {
			throw new NullPointerException("参数groupName不能为空值!");
		}
		TempUploadFile tuf = null;
		if(tempFiles.containsKey(concatKey(groupName , sessionId))) {
			tuf = tempFiles.get(concatKey(groupName , sessionId));
		} else {
			tuf = new TempUploadFile();
			tuf.setId(SequenceUtil.getUpperCaseUUID());
			tuf.setGroupName(concatKey(groupName , sessionId));
			tuf.setUploadTime(new Timestamp(System.currentTimeMillis()));
			tempFiles.put(concatKey(groupName , sessionId) , tuf);
		}
		MultiValueMap<String , MultipartFile> multiFileMap = request.getMultiFileMap();
		Iterator<String> iterator = multiFileMap.keySet().iterator();
		if(iterator.hasNext()) {
			String key = iterator.next();
			List<MultipartFile> list = multiFileMap.get(key);
			Size originalSize = new Size(-1 , -1);// 原始大小宽、高 规定为-1
			for(MultipartFile multipartFile : list) {
				String originalFilename = multipartFile.getOriginalFilename();
				String suffix = "." + StringUtils.substringAfterLast(originalFilename , ".");
				InputStream inputStream = multipartFile.getInputStream();
				boolean isUploadImg = isImg && !(imageFileSuffix.contains(StringUtils.lowerCase(suffix)));
				if(isUploadImg) {
					throw new Exception("文件格式错误");
				}
				String originalFileName = StringUtils.substringBeforeLast(originalFilename , ".");
				String originalImgFileName = SequenceUtil.getLowerCaseUUID() + "-" + originalFileName + "-";
				File originalImgFile = File.createTempFile(originalImgFileName , suffix);
				FileUtils.copyInputStreamToFile(inputStream , originalImgFile);
				if(imgMaxWidth != -1 || imgMaxHeight != -1) {
					BufferedImage read_originalImgFile = ImageIO.read(originalImgFile);
					int width = read_originalImgFile.getWidth();// 原始图宽度
					int height = read_originalImgFile.getHeight();// 原始图高度
					if(imgMaxWidth != -1 && imgMaxWidth < width) {
						IOUtils.closeQuietly(inputStream);
						FileUtils.deleteQuietly(originalImgFile);
						throw new Exception("图片最大宽度不得超过:" + imgMaxWidth);
					}
					if(imgMaxHeight != -1 && imgMaxHeight < height) {
						IOUtils.closeQuietly(inputStream);
						FileUtils.deleteQuietly(originalImgFile);
						throw new Exception("图片最大高度不得超过:" + imgMaxWidth);
					}
				}
				if(isUploadImg) {
					// 图片缩放
					if(sizes != null && !sizes.isEmpty()) {
						for(Size size : sizes) {
							thumbnail = ThumbnailsUtils.thumbnail(originalImgFile , false , size);
							if(thumbnail.getResCode() == 1) {
								// 压缩成功,添加到临时文件管理中,以便分发到文件服务器
								tuf.addTempFile(new TreeFile(SequenceUtil.getUpperCaseUUID() , null , thumbnail.getFile().getParent() , thumbnail.getFile().getPath() + suffix , thumbnail.getFile().getName() , false , size , true));
							} else {
								// 缩放失败,删除本次生成的临时文件
								File tempFileDir = originalImgFile.getParentFile();
								File [] tempFiles = tempFileDir.listFiles(new FilenameFilter() {
									
									@Override
									public boolean accept(File dir , String name) {
										return StringUtils.startsWith(name , originalImgFileName);
									}
								});
								if(tempFiles != null && tempFiles.length > 0) {
									for(File tf : tempFiles) {
										boolean delete = tf.delete();
										if(!delete) {
											tf.deleteOnExit();
										}
									}
								}
								throw new Exception("图片缩放失败!");
							}
						}
					}
					
				}
				tuf.addTempFile(new TreeFile(SequenceUtil.getUpperCaseUUID() , null , originalImgFile.getParent() , originalImgFile.getPath() , originalFileName + suffix , false , originalSize , false));
			}
		}
		return tuf;
	}
	
	/**
	 * 获取上传的临时文件
	 *
	 * @param groupName
	 *            临时文件分组名
	 * @return
	 */
	public static TempUploadFile getTempUploadFile(String groupName , String sessionId) {
		if(StringUtils.isBlank(groupName) || tempFiles.isEmpty()) {
			return null;
		}
		return tempFiles.get(concatKey(groupName , sessionId));
	}
	
	/**
	 * 清理临时文件
	 *
	 * @param groupName
	 *            临时文件分组名
	 */
	public static void clearTemUploadFile(String groupName , String sessionId) {
		ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
		cachedThreadPool.execute(new Runnable() {
			
			@Override
			public void run() {
				Timestamp nowTime = new Timestamp(System.currentTimeMillis());
				log.info(nowTime + " 开始清理临时文件...");
				if(!tempFiles.isEmpty() && tempFiles.containsKey(concatKey(groupName , sessionId))) {
					TempUploadFile tempUploadFile = tempFiles.get(concatKey(groupName , sessionId));
					List<TreeFile> fileList = tempUploadFile.getFileList();
					for(TreeFile tf : fileList) {
						deleteTempFile(tf);
					}
					tempFiles.remove(concatKey(groupName , sessionId));
				} else {
					log.info("未发现临时文件,清理结束!");
				}
			}
		});
		cachedThreadPool.shutdown();
	}
	
	/**
	 * 清理临时文件
	 *
	 * @param groupName
	 *            临时文件分组名
	 * @param sessionId
	 *            会话Id
	 * @param fileId
	 *            文件id
	 */
	public static void clearTemUploadFile(String groupName , String sessionId , String fileId) {
		ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
		cachedThreadPool.execute(new Runnable() {
			
			@Override
			public void run() {
				TempUploadFile tempUploadFile = FileUploadUtil.getTempUploadFile(groupName , sessionId);
				List<TreeFile> fileList = tempUploadFile.getFileList();
				if(fileList != null && !fileList.isEmpty()) {
					Optional<TreeFile> findFirst = fileList.parallelStream().filter(new Predicate<TreeFile>() {
						
						@Override
						public boolean test(TreeFile t) {
							return fileId.equals(t.getUuid());
						}
						
					}).findFirst();
					if(findFirst.isPresent()) {
						TreeFile treeFile = findFirst.get();
						String path = treeFile.getPath();
						File file = new File(path);
						if(!file.delete()) {
							file.deleteOnExit();
						}
						fileList.remove(treeFile);
					}
				}
			}
		});
		cachedThreadPool.shutdown();
	}
	
	/**
	 * 清理临时上传文件
	 *
	 * @param caacheTime
	 *            最大缓存时间(单位毫秒,凡是上传时间到调用该方法时的时间差超过该缓存时间的临时文件都将被清理)
	 */
	public static void clearTempUploadFile(long caacheTime) {
		ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
		cachedThreadPool.execute(new Runnable() {
			
			@Override
			public void run() {
				Timestamp nowTime = new Timestamp(System.currentTimeMillis());
				log.info(nowTime + " 开始清理临时文件...");
				if(!tempFiles.isEmpty()) {
					Set<String> keySet = tempFiles.keySet();
					for(String key : keySet) {
						TempUploadFile tempUploadFile = tempFiles.get(key);
						if(tempUploadFile != null && (tempUploadFile.getUploadTime().getTime() - nowTime.getTime()) > caacheTime) {// 临时文件缓存时间超过1小时的都清理
							List<TreeFile> filePathList = tempUploadFile.getFileList();
							for(TreeFile tf : filePathList) {
								deleteTempFile(tf);
							}
						}
					}
				} else {
					log.info("未发现临时文件,清理结束!");
				}
			}
		});
		cachedThreadPool.shutdown();
	}
	
	/**
	 * @author 谢进伟
	 * @description 文件分发回掉函数接口
	 * @createDate 2016年11月15日 下午1:35:16
	 */
	public static interface DistributedFileCallback {
		
		/**
		 * 保存文件分发记录
		 */
		public abstract void saveDistributedHistory(Boolean status);
	}
	
	/**
	 * ftp文件分发
	 *
	 * @param ip
	 *            ftp服务器ip
	 * @param host
	 *            ftp端口
	 * @param loginName
	 *            ftp等你用户名
	 * @param password
	 *            ftp登录密码
	 * @param rootDir
	 *            根目录
	 * @param relativeDir
	 *            文件保存相对于根目录的存放目录
	 * @param file
	 *            需要分发的文件
	 * @param callback
	 *            分发结果回调
	 */
	public static void ftpDistributedFile(String ip , String host , String loginName , String password , String rootDir , String relativeDir , File file , DistributedFileCallback callback) {
		ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
		cachedThreadPool.execute(new Runnable() {
			
			@Override
			public void run() {
				// TODO 文件分发
				// ...
				boolean status = false;
				callback.saveDistributedHistory(status);
			}
		});
	}
	
	/**
	 * http形式分发
	 *
	 * @param uploadUrl
	 *            上传路径
	 * @param rootDir
	 *            根目录
	 * @param relativeDir
	 *            文件保存相对于根目录的存放目录
	 * @param file
	 *            需要分发的文件
	 * @param callback
	 *            分发结果回调
	 */
	public static void httpDistributedFile(String uploadUrl , String rootDir , String relativeDir , File file , DistributedFileCallback callback) {
		ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
		cachedThreadPool.execute(new Runnable() {
			
			@Override
			public void run() {
				// TODO 文件分发
				// ...
				boolean status = false;
				callback.saveDistributedHistory(status);
			}
		});
	}
	
	private static String concatKey(String groupName , String sessionId) {
		return groupName + "-hsId" + sessionId;
	}
	
	/**
	 * 删除临时文件
	 *
	 * @param tf
	 *            临时文件
	 */
	private static void deleteTempFile(TreeFile tf) {
		File tempFile = new File(tf.getPath());
		if(tempFile.exists()) {
			boolean delete = tempFile.delete();
			if(delete) {
				log.info(new Timestamp(System.currentTimeMillis()) + " 删除临时文件:" + tf.getPath() + (delete ? "成功" : "失败"));
			} else {
				tempFile.deleteOnExit();// 应用正常退出时再次尝试删除临时文件
			}
		}
	}
}
